-- Desc: 订单支付成功， 减少锁定库存，增加销量


-- KEYS入参格式： 订单id_仓库id_skuId_lockNum, 订单id_仓库id_skuId_lockNum...
-- params格式： {{订单id, 仓库id, skuId, lockNum}, {订单id, 仓库id, skuId, lockNum}}
-- sku总库存key格式-redis： mall4j_product:sku_sock:skuId
-- sku区域库存key格式-redis： mall4j_product:sku_sock:skuId_仓库id
-- sku库存value格式-redis： 可售库存数量_锁定库存数量_销量_sku库存状态


-- 订单支付成功处理流程：
-- 步骤一  将参数字符串(KEYS)拆分为数组(params)， 拆分过程中根据库存操作类型处理：
--        拼接区域库存和总库存的key（mgetSkuKeys：用于批量查询库存数据。 总库存key格式  key名称:skuId;  区域库存key格式 key名称:skuId_仓库id）,
--            skuKey：根据数组中的存放的skuId以及仓库id，与批量获取的数据进行匹配（redis mget响应数据顺序对应查询时的key顺序）
--            skuKey中的数据有两种类型，一种是字符类型代表区域库存的skuId和仓库id，数据格式为： skuId_仓库id; 一种是数字类型代表总库存的skuId, 数据格式为：skuId
-- 步骤二  批量查询库存信息， 使用步骤一中拼接的key数组（mgetSkuKeys）进行查询
-- 步骤三  拆分步骤三mget获取的sku库存信息：
--            mget获取的sku库存信息格式为：可售库存数量_锁定库存数量_销量_sku库存状态
--            拆分成数组（skuStock）后的格式为：{可售库存数量, 锁定库存数量, 销量,sku库存状态}
--            库存集合（skuStocks）的数据格式为：map<skukey, sku库存数据>， 其中skukey还可以细分 -- 区域库存：skuId_仓库id（字符串）; 总库存：skuId（数字类型）
-- 步骤四  根据订单数据，减少sku锁定库存，增加对应的sku销量， 以及拼接sku库存变动结果（result）
-- 步骤五  判断步骤四中是否有改动sku库存信息，有改动则拼接要更新的sku库存key、value，并批量保存修改后的sku库存信息


-- ================ 以下为函数， 不属于主流程 ================
local function containsValue(list, value)
    for i = 1, #list do
        if list[i] == value then
            return false
        end
    end
    return true
end

-- ================ 开始提交订单锁定库存流程 ================

-- sku键数组
local skuKey = {}
-- 传参数据
local params = {}
-- 用于批量获取sku库存信息的key集合
local mgetSkuKeys = {}

-- 步骤一    入参 KEYS 订单id_仓库id_skuId_lockNum   订单id_仓库id_skuId_lockNum...
for index, paramStr in ipairs(KEYS) do
    -- {订单id, 仓库id, skuId, lockNum}
    local param = {}
    for value in string.gmatch(paramStr, "%d+") do
        table.insert(param, value)
    end
    table.insert(params, param)

    -- 避免一个sku同时进行多个操作时，查询和计算的库存数据操作，所以这里要对区域库存和总库存的key去重
    local key = param[3].."_"..param[2]
    local skuId = tonumber(param[3])
    -- 数组没有的sku数组再添加到数组，skuKey用于批量查询后的sku数据绑定
    -- 区域库存去重
    if param[2] ~= "0" and containsValue(skuKey, key) then
        -- sku区域库存key格式： key名称:skuId_仓库id
        table.insert(mgetSkuKeys, "mall4j_product:sku_sock:"..key)
        table.insert(skuKey, key)
    end
    -- 总库存去重
    if containsValue(skuKey, skuId) then
        -- sku总库存key格式： key名称:skuId
        table.insert(mgetSkuKeys, "mall4j_product:sku_sock:"..skuId)
        -- 用数字类型区分总库存
        table.insert(skuKey, skuId)
    end
end


-- 步骤二    通过mget获取所有key的值，call后面是可变长参数，unpack是将数组变成可变长参数
local skuStr = redis.call('mget', unpack(mgetSkuKeys));


-- 步骤三    拆分mget中获取到的sku库存信息。  skuStocks： map<skukey, sku库存数据>   skukey：区域库存=skuId_仓库id; 总库存=skuId
local skuStocks = {}
for index, key in ipairs(skuKey) do
    -- {可售库存数量, 锁定库存数量, 销量, sku库存状态}
    local skuStock = {}
    for value in string.gmatch(skuStr[index], "%d+") do
        table.insert(skuStock, tonumber(value))
    end
    -- sku库存列表
    skuStocks[key] = skuStock
end


local result = ""
local updateNum = 0
-- 步骤四    订单支付成功 - 减少sku锁定库存， 增加对应的sku销量
for i = 1,#params do
    -- 订单id_仓库id_skuId_lockNum_库存操作
    local param = params[i]
    -- 删除锁定标记 key-订单id_仓库id_skuId
    local isDelete = redis.call('del',"mall4j_product:order_sku_lock:"..param[1].."_"..param[2].."_"..param[3])

    -- 删除锁定标记成功才进行还原库存，让方法幂等, 没有删除锁定标记就跳过本次循环
    if isDelete > 0 then
        -- 可售库存数量_锁定库存数量_销量_sku库存状态
        local skuPointStock = skuStocks[param[3].."_"..param[2]]
        local skuStock = skuStocks[tonumber(param[3])]

        local changeStock = tonumber(param[4])

        -- 修改sku库存记录
        if skuPointStock then
            -- 减少sku锁定区域库存
            skuPointStock[2] = tonumber(skuPointStock[2]) - changeStock
            -- 增加sku区域销量
            skuPointStock[3] = tonumber(skuPointStock[3]) + changeStock
            -- 订单id_仓库id_skuId_剩余库存,订单id_仓库id_skuId_剩余库存...
            result = result..param[1].."_"..param[2].."_"..param[3].."_"..skuPointStock[1]..","
        end

        -- 减少sku锁定总库存
        skuStock[2] = tonumber(skuStock[2]) - changeStock
        -- 增加sku总销量
        skuStock[3] = tonumber(skuStock[3]) + changeStock

        result = result..param[1].."_0_"..param[3].."_"..skuStock[1]..","
        updateNum = updateNum + 1
    end
end


-- 用于批量设置sku库存信息的key和value集合
local msetSkuKV = {}
-- 步骤五    如果更新数量大于0，添加sku库存的key、value
if updateNum > 0 then
    for index, key in ipairs(skuKey) do
        -- 库存信息 {可售库存数量, 锁定库存数量, 销量, sku库存状态}
        local skuStock = skuStocks[key]
        -- mset sku库存key   总库存格式：  key名称:skuId； 区域库存格式：  key名称:skuId_仓库id
        table.insert(msetSkuKV, "mall4j_product:sku_sock:"..key)
        -- mset sku库存value   格式：  可售库存数量_锁定库存数量_销量_sku库存状态
        table.insert(msetSkuKV, skuStock[1].."_"..skuStock[2].."_"..skuStock[3].."_"..skuStock[4])
    end

    -- 修改回redis
    redis.call('mset',unpack(msetSkuKV))
end


return result
